﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml.Linq;
using eTerm.NetSocket;
using System.Reflection;
using System.IO;
using System.Text.RegularExpressions;
using System.ComponentModel;

namespace eTerm.PlugIn.NCore
{
    /// <summary>
    /// 插件基类
    /// </summary>
    public abstract class BasePlugIn:IPlugIn
    {

        #region 私有变量定义
        Timer schedule = null;
        private DateTime? _PreExecuteDate = DateTime.Now;
        private bool __disposed = false;
        protected XElement XmlRoot = null;
        private int __executeOrder = 0;
        #endregion


        #region 计划任务
        /// <summary>
        /// 执行计划
        /// </summary>
        protected virtual Frequency ExecuteFrequency { get; set; }


        /// <summary>
        /// 开启计划任务
        /// </summary>
        private void PlugInSchedule()
        {
            if (ExecuteFrequency == null || schedule!=null) return;

            switch (ExecuteFrequency.Occurs)
            {          //暂时只处理天间隔类型
                case (int)OccursType.DAILY:
                    if (ExecuteFrequency.OccursEveryMinutes > 0)
                    {                              //间隔执行
                        int scheduleSeconds = ExecuteFrequency.OccursEveryMinutes * 60 * 1000;
                        schedule = new Timer(new TimerCallback((o) =>
                        {
                            //FireOnNotify(PlugInName, EventType.INFO, string.Format(@"启动[{0}]计划任务。", PlugInName));
                            //Execute();
                            FireOnFrequency();
                        }), null, scheduleSeconds, scheduleSeconds);
                    }
                    else if (!string.IsNullOrEmpty(ExecuteFrequency.OccursOnceAt))
                    {             //定点执行
                        DateTime nextSchedule = DateTime.Parse(string.Format(@"{0} {1}", DateTime.Now.AddDays(1).ToString(@"yyyy-MM-dd"), ExecuteFrequency.OccursOnceAt));
                        schedule = new Timer(new TimerCallback((o) =>
                        {
                            //FireOnNotify(PlugInName, EventType.INFO, string.Format(@"启动[{0}]计划任务。", PlugInName));
                            //Execute();
                            FireOnFrequency();
                        }), null, ((TimeSpan)(nextSchedule - DateTime.Now)), new TimeSpan(24, 0, 0));
                    }
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 读取计划任务安排
        /// </summary>
        private void SetPlugInSchedule()
        {
            try
            {
                XElement FrequencyNode = XmlRoot.Elements().FirstOrDefault(l => l.Name.LocalName.ToLower() == @"Frequency".ToLower());
                if (FrequencyNode == null) return;
                ExecuteFrequency = SetEntity<Frequency>(@"Frequency");
            }
            catch (Exception ex)
            {
                FireOnException(ex);
            }
        }
        #endregion

        #region IPlugIn Members

        /// <summary>
        /// 差件名称
        /// </summary>
        /// <value>用于日志记录的插件名称.</value>
        public abstract string PlugInName { get; }

        /// <summary>
        /// 插件执行顺序
        /// </summary>
        /// <value>The execute order.</value>
        public virtual int ExecuteOrder
        {
            get { return __executeOrder; }
            protected set { __executeOrder = value; }
        }

        /// <summary>
        /// 插件执行主方法
        /// </summary>
        public void Execute()
        {
            FireOnExecuting();
            try
            {
                LoadPlugInXml();
                OpenDatabase();
                InitCommonSetting();
                SetCustomSetup();
                ExecutePlugIn();
            }
            catch (Exception ex) {
                FireOnException(ex, this.PlugInName);
            }
            AfterExecutePlugIn();
        }

        /// <summary>
        /// 完成插件后调用方法.
        /// </summary>
        protected virtual void AfterExecutePlugIn() {
            FireOnExecuted();
        }

        /// <summary>
        /// 插件内部方法.
        /// </summary>
        protected abstract void ExecutePlugIn();

        /// <summary>
        /// 插件开始执行
        /// </summary>
        public event EventHandler<PlugInEventArgs> OnExecuting;

        /// <summary>
        /// 插件执行结束
        /// </summary>
        public event EventHandler<PlugInEventArgs> OnExecuted;

        /// <summary>
        /// 插件执行异常
        /// </summary>
        public event EventHandler<PlugInEventArgs> OnException;

        /// <summary>
        /// 插件执行通知
        /// </summary>
        public event EventHandler<PlugInEventArgs> OnNotify;

        /// <summary>
        /// 插件执行任务通知
        /// </summary>
        public event EventHandler<PlugInEventArgs> OnFrequency;


        #endregion

        #region 配置相关
        /// <summary>
        /// 上一次执行时间.
        /// </summary>
        /// <value>上一次执行时间.</value>
        protected DateTime? PreExecuteDate
        {
            get
            {
                return _PreExecuteDate??DateTime.Now;
            }
            private set
            {
                _PreExecuteDate = value;
            }
        }


        /// <summary>
        /// 加载配置文件.
        /// </summary>
        private void LoadPlugInXml() {
            string SetupFileName = Path.Combine(AssemblyPath, SetupXmlFile);
            if (!File.Exists(SetupFileName)) return;
            XmlRoot = XElement.Load(SetupFileName);
        }


        /// <summary>
        /// 获取Xml配置节点值.
        /// </summary>
        /// <param name="XmlNodeName">节点名称.</param>
        /// <returns>System.String.</returns>
        protected virtual string GetElementValue(string XmlNodeName)
        {
            string Result = string.Empty;
            try
            {
                Result = XmlRoot.Elements().FirstOrDefault(l => l.Name.LocalName.ToLower() == XmlNodeName.ToLower()).Value;
            }
            catch { }
            return Result;
        }

        /// <summary>
        /// 设置Xml配置节点值.
        /// </summary>
        /// <param name="XmlNodeName">节点名称.</param>
        /// <param name="NodeValue">节点值.</param>
        protected virtual void SetElementValue(string XmlNodeName, string NodeValue)
        {
            string SetupFileName = Path.Combine(AssemblyPath, SetupXmlFile);
            try
            {
                XElement Node = XmlRoot.Elements().FirstOrDefault(l => l.Name.LocalName.ToLower() == XmlNodeName.ToLower());
                if (Node != null)
                    Node.Value = NodeValue;
                else
                {
                    XmlRoot.Add(new XElement(XmlNodeName, NodeValue));
                }
                XmlRoot.Save(SetupFileName);
            }
            catch { }
        }



        /// <summary>
        /// 初始化数据库.
        /// </summary>
        private void OpenDatabase() {
            if (XmlRoot == null) return;
            string DbString = GetElementValue(@"ConnectionString");
            string ProviderName = GetElementValue(@"DbProviderName");
            if (string.IsNullOrEmpty(DbString)) return;
            OpenDatabase(ProviderName, DbString);
        }



        /// <summary>
        /// 初始化自定义配置项
        /// </summary>
        protected virtual void SetCustomSetup() { 
        
        }

        /// <summary>
        /// 初始化通用配置项
        /// </summary>
        private void InitCommonSetting()
        {
            __executeOrder = string.IsNullOrEmpty(GetElementValue(@"ExecuteOrder")) ? 0 : int.Parse(GetElementValue(@"ExecuteOrder"));
            _PreExecuteDate = !string.IsNullOrEmpty(GetElementValue(@"PreExecuteDate")) ? DateTime.Parse(GetElementValue(@"PreExecuteDate")) as DateTime? : null;
        }




        /// <summary>
        /// 初始化数据库[重写部份].
        /// </summary>
        /// <param name="ProviderName">驱动名.</param>
        /// <param name="DbString">连接字符串.</param>
        protected virtual void OpenDatabase(string ProviderName,string DbString){
            
        }


        /// <summary>
        /// 插件引用地址.
        /// </summary>
        /// <value>插件引用地址.</value>
        protected string AssemblyPath
        {
            get
            {
                return Path.GetDirectoryName(this.GetType().Assembly.Location);
            }
        }

        /// <summary>
        /// 插件配置文件名.
        /// </summary>
        /// <value>插件配置文件名.</value>
        protected virtual string SetupXmlFile {
            get {
                return string.Format(@"{0}.{1}", this.GetType().Name, @"xml");
            }
        }
        #endregion

        #region 辅助方法
        protected string GetEnumName(Enum value) //where T:enum
        {
            StringBuilder sb = new StringBuilder();
            FlagsAttribute attr = Attribute.GetCustomAttribute(value.GetType(), typeof(FlagsAttribute)) as FlagsAttribute;
            if (attr == null)
            {
                return GetDescription(value);
            }
            int t = (int)Convert.ChangeType(value, typeof(int));
            //Enum x = (Enum)Convert.ChangeType(value, typeof(Enum));
            Enum.GetValues(value.GetType()).Cast<Enum>().ToList().ForEach(l =>
            {
                Enum y = Convert.ChangeType(l, typeof(Enum)) as Enum;
                int x = (int)Convert.ChangeType(l, typeof(Enum));
                if ((t & x) != 0)
                {
                    string v = GetDescription(l);
                    if (!string.IsNullOrEmpty(v))
                        sb.AppendFormat(@"[{0}]", GetDescription(y));
                }
            });
            return sb.ToString();
        }

        /// <summary>
        /// 从Xml加载实体.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="XElementName">Name of the X element.</param>
        /// <returns>``0.</returns>
        protected T SetEntity<T>(string XElementName) where T : new()
        {
            T result = new T();
            try
            {
                XElement XElementNode = XmlRoot.Elements().FirstOrDefault(l => l.Name.LocalName.ToLower() == XElementName.ToLower());
                if (XElementNode == null) return default(T);
                foreach (PropertyInfo property in typeof(T).GetProperties())
                {
                    XElement Node = XElementNode.Elements().FirstOrDefault(l => l.Name.LocalName.ToLower() == property.Name.ToLower());
                    if (Node == null) continue;
                    try
                    {
                        property.SetValue(result, Convert.ChangeType(Node.Value, property.PropertyType), null);
                    }
                    catch
                    {
                        FireOnNotify(property.Name, EventType.ERROR, string.Format(@"对像 {0} 属性 {1} 读取失败", typeof(T).Name, property.Name));
                    }
                }
            }
            catch (Exception ex)
            {
                FireOnNotify(ex.GetType().Name, EventType.ERROR, ex.Message);
            }
            return result;
        }



        /// <summary>
        /// 返回指定枚举值的描述（通过 
        /// <see cref="System.ComponentModel.DescriptionAttribute"/> 指定）。
        /// 如果没有指定描述，则返回枚举常数的名称，没有找到枚举常数则返回枚举值。
        /// </summary>
        /// <param name="value">要获取描述的枚举值。</param>
        /// <returns>指定枚举值的描述。</returns>
        protected string GetDescription(Enum value)
        {
            Type enumType = value.GetType();
            {
                MemberInfo[] mi = value.GetType().GetMember(value.ToString());
                if (mi != null && mi.Length > 0)
                {
                    DescriptionAttribute attr = Attribute.GetCustomAttribute(mi[0], typeof(DescriptionAttribute)) as DescriptionAttribute;
                    if (attr != null)
                    {
                        return attr.Description;
                    }
                }
            }
            return string.Empty;
        }

        /// <summary>
        /// 生成唯一的字符串
        /// </summary>
        /// <returns>唯一的字符串</returns>
        protected string GenUniqueString()
        {
            string readyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            string rtn = "";
            string guid = Guid.NewGuid().ToString();
            guid = guid.Replace("-", "");
            for (int i = 0; i < 8; i++)
            {
                int t = Convert.ToInt32(guid[i]) + Convert.ToInt32(guid[i + 8]) + Convert.ToInt32(guid[i + 16]) + Convert.ToInt32(guid[i + 24]);
                rtn += readyStr[t % 35];
            }

            return new Regex(@"(^\d)", RegexOptions.IgnoreCase).IsMatch(rtn) ? GenUniqueString() : rtn;
        }



        /// <summary>
        /// 转换邮件模板数据.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model">The model.</param>
        /// <param name="RootName">Name of the root.</param>
        /// <returns>System.String.</returns>
        protected string ToXml<T>(T model, string RootName)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat(@"<{0}>", RootName);
            foreach (PropertyInfo p in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
            {
                try
                {
                    object v = p.GetValue(model, null);
                    sb.AppendFormat(@"<{0}><![CDATA[{1}]]></{0}>", p.Name, v);
                }
                catch { }
            }
            sb.AppendFormat(@"</{0}>", RootName);
            return sb.ToString();
        }

        /// <summary>
        /// 转换邮件模板数据.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model">The model.</param>
        /// <returns>System.String.</returns>
        protected string ToXml<T>(T model)
        {
            return ToXml(model, typeof(T).Name);
        }
        #endregion


        #region 事件调用
        /// <summary>
        /// 开始插件调用事件通知.
        /// </summary>
        protected void FireOnExecuting()
        {
            if (this.OnExecuting != null)
                OnExecuting(this, new PlugInEventArgs(this));
        }

        /// <summary>
        /// 结束插件调用事件通知.
        /// </summary>
        protected virtual void FireOnExecuted()
        {
            if (this.OnExecuted != null)
                OnExecuted(this, new PlugInEventArgs(this));
            SetPlugInSchedule();
            PlugInSchedule();
            SetElementValue(@"PreExecuteDate", DateTime.Now.ToString(@"yyyy-MM-dd HH:mm:ss"));
        }


        /// <summary>
        /// 插件异常事件通知.
        /// </summary>
        /// <param name="ex">The ex.</param>
        /// <param name="BusinessNo">The business no.</param>
        protected virtual void FireOnException(Exception ex, string BusinessNo)
        {
            if (OnException != null)
                OnException(this, new PlugInEventArgs(this, BusinessNo, EventType.ERROR, string.Format(@"Message:{0}
StackTrace:{1}", ex.Message, ex.StackTrace)));
        }

        /// <summary>
        /// 插件执行任务通知.
        /// </summary>
        protected void FireOnFrequency() {
            if (this.OnFrequency != null)
                OnFrequency(this, new PlugInEventArgs(this));
        }

        /// <summary>
        /// 插件异常事件通知.
        /// </summary>
        /// <param name="ex">The ex.</param>
        protected virtual void FireOnException(Exception ex) {
            FireOnException(ex, PlugInName);
        }

        /// <summary>
        /// 开始插件调用事件通知.
        /// </summary>
        /// <param name="BusinessNo">对象编号.</param>
        /// <param name="NotifyType">通知类型.</param>
        /// <param name="NotifyMessage">通知信息.</param>
        protected virtual void FireOnNotify(string BusinessNo, EventType NotifyType, string NotifyMessage)
        {
            if (this.OnNotify != null)
                OnNotify(this, new PlugInEventArgs(this, BusinessNo, NotifyType, NotifyMessage));
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// 实现IDisposable中的Dispose方法
        /// </summary>
        public void Dispose()
        {
            //必须为true
            Dispose(true);
            //通知垃圾回收机制不再调用终结器（析构器）
            GC.SuppressFinalize(this);
            //if (!ImmediatelyExit)
            FireOnExecuted();
        }

        /// <summary>
        /// 必须，以备程序员忘记了显式调用Dispose方法
        /// </summary>
        ~BasePlugIn()
        {
            //必须为false
            Dispose(false);
        }

        /// <summary>
        /// 释放插件资源
        /// </summary>
        protected virtual void DisposePlugIn(){

            if (schedule != null)
            {
                schedule.Dispose();
                schedule = null;
            }

        }


        /// <summary>
        /// 非密封类修饰用protected virtual
        /// 密封类修饰用private
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (__disposed)
            {
                return;
            }
            if (disposing)
            {
                // 清理托管资源
                DisposePlugIn();
                //}
                //// 清理非托管资源
                //if (nativeResource != IntPtr.Zero)
                //{
                //    Marshal.FreeHGlobal(nativeResource);
                //    nativeResource = IntPtr.Zero;
                //}
                //让类型知道自己已经被释放
                __disposed = true;
            }
        }
        #endregion
    }
}
